package com.example.musicplayer.dao;

import java.util.ArrayList;
import java.util.List;
import java.util.Random;

import android.content.ContentResolver;
import android.content.ContentValues;
import android.content.Context;
import android.database.Cursor;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.net.Uri;
import android.os.Environment;
import android.provider.MediaStore;
import android.util.Log;

import com.example.musicplayer.dto.Song;

/**
 * Class that finds all music whether on SD card or Internal Memory
 * @author Jonathan
 */
public class MusicHandler extends SQLiteOpenHelper implements IMusicHandler {
	public static final String TABLENAME = "shuffled_songs";
	public static final String TABLENAME2 = "song_playlist";
	private static final String SONGNAME = "title";
	private static final String ARTIST = "artist";
	private static final String ALBUM = "album";
	private static final String SONGDATA = "song_path";
	private static final String ALBUMID = "album_id";
	private static final String SONGID = "song_id";
	private static final String PLAYPOSITION = "amount_played";
	//A List that holds song objects, MASTER SONG LIST that prevents from unnecessary querying
	List<Song> songList = new ArrayList<Song>();
	//A custom playlist that holds user picked songs (later intergration)
	List<Song> playList = new ArrayList<Song>();
	//list of random songs
	List<Song> shuffledList = new ArrayList<Song>();
	//results list of searched songs
	List<Song> searchedSongsList = new ArrayList<Song>();
	Cursor cursor;
	//URI for media source
	private Uri mediaUri;
	private Uri albumUri;
	ContentResolver contentResolver;
	// a true/false variable thats used by methods that use cursor
	private boolean mState;
	//handles shuffle function
	boolean isShuffled;
	
	
	public MusicHandler(ContentResolver cr, boolean shuffled, Context ctx) {
		super(ctx, "musicHistory", null, 1);
		//sets shuffled state
		isShuffled = shuffled;
		contentResolver = cr;
		//called to check media state and sets it once
		MediaStateChecker();
		//gets called to populate musiclist cursor
		RetrieveMusic();
		
    }
	
	/**
	 * Method that only finds music on device using cursors, then calls addSongs
	 * 
	 * @return a cursor object containing data about music 
	 */
	@Override
	public void RetrieveMusic() {
		if(mState){
			Log.i("SD State", "SD card found. Now using SD card.");
			mediaUri = android.provider.MediaStore.Audio.Media.EXTERNAL_CONTENT_URI;
		}
		else{
			Log.i("SD State", "No SD card detected. Now using internal storage");
			mediaUri = android.provider.MediaStore.Audio.Media.INTERNAL_CONTENT_URI;
		}
		cursor = cursorHandler(mediaUri,null,null);
		if(cursor == null){
			Log.e("Empty Cursor", "Cursor was not loaded. Error occured!");
			//closes cursor if there is an error
			cursor.close();
			return;
		}
		if(!cursor.moveToFirst())
		{
		 Log.e("Empty", "No music found on SD/Card");
		 //closes cursor if the is no music
		 cursor.close();
		 return;
		}
		else{
			//only adds music to songlist is there has been new music added to device
			if(songList.size() < cursor.getCount ())
			{
				cursor.moveToPosition(songList.size());
				//call to addsong method
				AddSongs(cursor);
				cursor.close();
			}
			//cursor.close();
       }
		cursor.close();
	}
	
	/**
	 * method that provides a cursor, since more than two method needed a cursor
	 * I refactored a resuable method - Code smell -1
	 * 
	 * @param mediaUri, URL to be queried(same thing as a table)
	 * @param columns, columns from the URI to return
	 * @param selection, the search criteria to use to bring back the values from the columns (same thing as where clause)
	 *
	 * @return cursor, cursor will all the values from columns
	 */
	public Cursor cursorHandler(Uri mediaUri,String [] columns ,String selection) {
		//an array that tells the curser of what data about the music to bring back such as data path/name/song artist.
		if(columns == null)
		{
			columns = new String [] {
			MediaStore.Audio.Media._ID,
			MediaStore.Audio.Media.DATA,
			MediaStore.Audio.Media.TITLE,
			MediaStore.Audio.Media.ALBUM,
			MediaStore.Audio.Media.ALBUM_ID,
			MediaStore.Audio.Media.ARTIST,
			MediaStore.Audio.Media.DURATION
			};
		}
		if(selection == null)
			selection = MediaStore.Audio.Media.IS_MUSIC + " = 1";
		//loads cursor with music data from uri source.
		cursor = contentResolver.query(mediaUri, columns, selection, null, null);
		return cursor;
	}
	
	/**
	 * Adds all songs from cursor into song objects and loads them into songlist, and a hashmap
	 * that will be used for searhing method
	 * 
	 * @param musicCursor, cursor that contains music metadata and adds to master songlist
	 * 
	 * @return songlist, collections of song objects
	 */
	public void AddSongs(Cursor musicCursor) {
		do{
			Song song = new Song();
			song.setAlbumID(musicCursor.getLong(musicCursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID)));
			song.setSongAlbum(musicCursor.getString(musicCursor.getColumnIndex(MediaStore.Audio.Media.ALBUM)));
			song.setSongArtist(musicCursor.getString(musicCursor.getColumnIndex(MediaStore.Audio.Media.ARTIST)));
			song.setSongName(musicCursor.getString(musicCursor.getColumnIndex(MediaStore.Audio.Media.TITLE)));
			song.setSongPath(musicCursor.getString(musicCursor.getColumnIndex(MediaStore.Audio.Media.DATA)));
			song.setSongDuration(musicCursor.getString(musicCursor.getColumnIndex(MediaStore.Audio.Media.DURATION)));
			//prevent music from being added twice
			if(!songList.contains(song))
				songList.add(song);
		}while(musicCursor.moveToNext());
				String tag = null;
				//final call to close cursor
				musicCursor.close();
				//igonore this line for  debugging
				Log.i(tag,Integer.toString(songList.size()));
				if(isShuffled == true)
				Shuffler();
	}
	
	/**
	 * Returns a content resolver object that is used to intantiate Content Resolver in retrieve method
	 * 
	 * @return ContentReolver
	 */
	@SuppressWarnings("unused")
	private ContentResolver getContentResolver() {
		return contentResolver;
	}
	
	/** 
	 * Retrieves album art using the album ID
	 * 
	 * @params long albumID, the unique album ID provided my the content provider
	 * 
	 * @return returns albumart filepath in a string.
	 */
	@Override
	public String RetrieveAlbumArt(long albumID) {
		//(imcomplete) will set default blank album image if none exist with song
		String defaultImage = "android.resource://com.example.musicplayer/raw/albumplaceholder";
		if(mState){
			albumUri = android.provider.MediaStore.Audio.Albums.EXTERNAL_CONTENT_URI;
		}
		else{
			albumUri = android.provider.MediaStore.Audio.Albums.INTERNAL_CONTENT_URI;
		}
		//a string that limits the query to only return album arts matching the album ID
		String selection = MediaStore.Audio.Albums._ID + "=" + albumID;
		
		//only brings baclk one column opposed to all columns, more effiecent
		String [] albumcharacteristics = {
				MediaStore.Audio.Albums.ALBUM_ART
		};
		cursor = cursorHandler(albumUri,albumcharacteristics,selection);
		cursor.moveToPosition(0);
		if(!cursor.isNull(cursor.getColumnIndex("album_art"))){
		 //a string that holds the album art file path
		String artUri = cursor.getString(cursor
				.getColumnIndex("album_art"));
		cursor.close();
			return artUri;
		}else{
			cursor.close();
			return defaultImage;
		}
	}
	
	/**
	 * Checks whether there is a External SD card inserted
	 * 
	 * @return returns a true or false based on media state
	 */
	@Override
	public boolean MediaStateChecker() {
		//gets external media state (either media_mounted or media_mounted)
		String mediaState = Environment.getExternalStorageState();
		//checks whether or not a external SD is attached by comparing current external storage state.
		//and returns true if attached.
		if(Environment.MEDIA_MOUNTED.equals(mediaState)){
			return mState = true;
		}
		//returns false if no SD card present or error with SD card
		else
		return mState = false;
	}
	
	/**
	 * Method that gets a saved list of songs objects to be used to play music
	 * 
	 * @return List, list of songs either shuffled or normal sort order
	 */
	@Override
	public List<Song> GetMusicList(boolean isshuffled1) {
		if(isShuffled == false)
		return songList;
		else
		return shuffledList;
	}
	/**
	 * Method that saves a List of song object either shuffled list or playlist
	 * 
	 * @param list, collection of song objects
	 * @param tableName, table to save List to
	 * 
	 */
	@Override
	public void SaveSongList(List<Song> list, String tableName) {
		// TODO Auto-generated method stub	
		for(Song song: list){
			
			ContentValues songCV = new ContentValues();
			songCV.put(SONGNAME, song.getSongName());
			songCV.put(ARTIST, song.getSongArtist());
			songCV.put(ALBUM, song.getSongAlbum());
			songCV.put(ALBUMID, song.getAlbumID());
			songCV.put(SONGDATA, song.getSongPath());
			
			long sqliteId = getWritableDatabase().insert(TABLENAME, SONGNAME, songCV);
		}
		
	}
	
	/**
	 * Method that searches for a song recursively based off user text
	 * 
	 * @param songSearchData
	 * 
	 * @return searchedSong, instances of searched song if found otherwise returns null
	 */
	@Override
	public Song SongSearch(String songSearchData){
		String searchCriteria = "where" + MediaStore.Audio.Media.TITLE + "LIKE '%" + songSearchData
				+ "%' OR " + MediaStore.Audio.Media.ARTIST + "LIKE '%" + songSearchData
				+ " %' OR" + MediaStore.Audio.Media.TITLE + "LIKE '%" + songSearchData; 
		cursor = cursorHandler(mediaUri,null,searchCriteria);
		if(cursor == null){
			Log.e("Empty Cursor", "Cursor was not loaded. Error occured!");
			cursor.close();
			return null;
		}
		if(!cursor.moveToFirst())
		{
		 Log.e("Empty", "No music found on match text entered");
		 cursor.close();
		 return null;
		}
		do{
			Song song = new Song();
			song.setAlbumID(cursor.getLong(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM_ID)));
			song.setSongAlbum(cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ALBUM)));
			song.setSongArtist(cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.ARTIST)));
			song.setSongName(cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.TITLE)));
			song.setSongPath(cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DATA)));
			song.setSongDuration(cursor.getString(cursor.getColumnIndex(MediaStore.Audio.Media.DURATION)));
			searchedSongsList.add(song);
		}while(cursor.moveToNext());
		cursor.close();
		Log.i("All Songs", "All the songs that match the search Criteria");
		return null;
	}
	
	/**
	 * Method that handles making a custom playlist
	 * 
	 * @param Song, user selected song that's to be added to playlist
	 */
	@Override
	public void CreatePlayList(Song song) {
		playList.add(song);
	}
	
	/**
	 * Method that returns custom playlist
	 * 
	 * @return playlist, custom List<Song> of user selected songs
	 */
	public List<Song> GetPlayList(){
		//if playlist is not empty returns the List
		if(!playList.isEmpty())
		return playList;
		//if playlist is empty returns null, which has to be handled in activity
		else
		return null;
	}
	
	/**
	 * Method that generates a random list of songs, ensures that no so is entered twice
	 * 
	 * @return shuffledList, a list of randomly choosen songs
	 */
	public List<Song> Shuffler(){
		Random rn = new Random();
		//variable to hold random numbers
		int randomNumber;
		// adds randomly selected songs to list for as long as the song doesn't occur in the list
		do{
				//generates random number based off size of master song list or could throughs error
				randomNumber = rn.nextInt(songList.size() + 1 - 1);
				Song song = songList.get(randomNumber);
				if(!shuffledList.contains(song))
				{
				//adds random number to array list outside of do/while loop inorder to stop infinite loop
				shuffledList.add(song);
				}
		//this statement repeat based off numbers in array compared to size of songList
		}while(shuffledList.size() < songList.size());
		//igonore this line for  debugging
		Log.i("Count",Integer.toString(songList.size()));
		return shuffledList;
	}

	@Override
	public void onCreate(SQLiteDatabase dataBase) {
		//table that will hold playlist 
		String createTable = "CREATE TABLE " + TABLENAME + "(" + SONGID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 
		+ SONGNAME + "TEXT, " + ARTIST + "TEXT, " + ALBUM + "TEXT, " + SONGDATA + "TEXT, " + ALBUMID + "LONG, "
		+ PLAYPOSITION + "INTEGER); ";
		dataBase.execSQL(createTable);
		//table that will hold playlist
		String createTable2 = "CREATE TABLE " + TABLENAME2 + "(" + SONGID + " INTEGER PRIMARY KEY AUTOINCREMENT, " 
				+ SONGNAME + "TEXT, " + ARTIST + "TEXT, " + ALBUM + "TEXT, " + SONGDATA + "TEXT, " + ALBUMID + "LONG, "
				+ PLAYPOSITION + "INTEGER); ";
				dataBase.execSQL(createTable2);
	}

	@Override
	public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
		// TODO Auto-generated method stub
		
	}
	
}
